﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Xml.XPath;

namespace ComicsEditor
{
    internal struct FillInfo
    {
        public Brush Fill;

        public FillInfo(Color Color)
        {
            this.Fill = new SolidBrush(Color);
        }

        public FillInfo(Brush Fill)
        {
            this.Fill = Fill;
        }

        public override bool Equals(object obj)
        {
            Brush objFill = ((FillInfo)obj).Fill;
            if (Fill == null && objFill == null)
                return true;
            if (Fill != null && objFill == null)
                return false;
            if (Fill == null && objFill != null)
                return false;
            return (Fill as SolidBrush).Color == (objFill as SolidBrush).Color;
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public static bool operator==(FillInfo a, FillInfo b)
        {
            return a.Equals(b);
        }

        public static bool operator!=(FillInfo a, FillInfo b)
        {
            return !a.Equals(b);
        }
    }

    internal struct StrokeInfo
    {
        public Pen Stroke;

        public StrokeInfo(Color Color, float Width)
        {
            this.Stroke = new Pen(Color, Width);
        }

        public StrokeInfo(Pen Stroke)
        {
            this.Stroke = Stroke;
        }

        public override bool Equals(object obj)
        {
            Pen objStroke = ((StrokeInfo)obj).Stroke;
            if (Stroke == null && objStroke == null)
                return true;
            if (Stroke != null && objStroke == null)
                return false;
            if (Stroke == null && objStroke != null)
                return false;
            return Stroke.Color == objStroke.Color && Math.Abs(Stroke.Width - objStroke.Width) < 1e-3;
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public static bool operator==(StrokeInfo a, StrokeInfo b)
        {
            return a.Equals(b);
        }

        public static bool operator!=(StrokeInfo a, StrokeInfo b)
        {
            return !a.Equals(b);
        }
    }

    public class TextFormatted
    {
        public readonly Text Text;

        private List<char> chars = new List<char>();
        private List<string> fontName = new List<string>();
        private List<float?> fontSize = new List<float?>();
        private List<bool?> bold = new List<bool?>();
        private List<bool?> italic = new List<bool?>();
        private List<FillInfo?> fill = new List<FillInfo?>();
        private List<StrokeInfo?> stroke = new List<StrokeInfo?>();

        private List<TextLine> lines = new List<TextLine>();

        public TextFormatted(Text Text)
        {
            this.Text = Text;
        }

        public int Count
        {
            get
            {
                return lines.Count;
            }
        }

        public TextLine this[int index]
        {
            get
            {
                return lines[index];
            }
        }

        public string Value
        {
            get
            {
                return new string(chars.ToArray());
            }
            set
            {
                Replace(0, chars.Count, value);
            }
        }

        public void Replace(int start, int length, string newText)
        {
            chars.RemoveRange(start, length);
            chars.InsertRange(start, newText);

            string curFontName = GetFontName(start + length - 1);
            float curFontSize = GetFontSize(start + length - 1);
            bool curBold = GetBold(start + length - 1);
            bool curItalic = GetItalic(start + length - 1);
            FillInfo curFill = GetFillInfo(start + length - 1);
            StrokeInfo curStroke = GetStrokeInfo(start + length - 1);

            int i = 0;
            if (length > 0 && newText.Length > 0)
                i++;
            fontName.RemoveRange(start + i, length - i);
            fontSize.RemoveRange(start + i, length - i);
            bold.RemoveRange(start + i, length - i);
            italic.RemoveRange(start + i, length - i);
            fill.RemoveRange(start + i, length - i);
            stroke.RemoveRange(start + i, length - i);
            for (; i < newText.Length; i++)
            {
                fontName.Insert(start + i, null);
                fontSize.Insert(start + i, null);
                bold.Insert(start + i, null);
                italic.Insert(start + i, null);
                fill.Insert(start + i,  null);
                stroke.Insert(start + i, null);
            }

            i += start;
            if (length > 0 && i < chars.Count)
            {
                string newFontName = GetFontName(i - 1);
                float newFontSize = GetFontSize(i - 1);
                bool newBold = GetBold(i - 1);
                bool newItalic = GetItalic(i - 1);
                FillInfo newFill = GetFillInfo(i - 1);
                StrokeInfo newStroke = GetStrokeInfo(i - 1);
                if (fontName[i] == null && newFontName != curFontName)
                    fontName[i] = curFontName;
                if (fontSize[i] == null && newFontSize != curFontSize)
                    fontSize[i] = curFontSize;
                if (bold[i] == null && newBold != curBold)
                    bold[i] = curBold;
                if (italic[i] == null && newItalic != curItalic)
                    italic[i] = curItalic;
                if (fill[i] == null && newFill != curFill)
                    fill[i] = curFill;
                if (stroke[i] == null && newStroke != curStroke)
                    stroke[i] = curStroke;
                if (fontName[i] != null && fontName[i] == newFontName)
                    fontName[i] = null;
                if (fontSize[i] != null && fontSize[i] == newFontSize)
                    fontSize[i] = null;
                if (bold[i] != null && bold[i] == newBold)
                    bold[i] = null;
                if (italic[i] != null && italic[i] == newItalic)
                    italic[i] = null;
                if (fill[i] != null && fill[i] == newFill)
                    fill[i] = null;
                if (stroke[i] != null && stroke[i] == newStroke)
                    stroke[i] = null;
            }

            Text.Invalidate();
        }

        private void setList<T>(List<T> list, int start, int length, T prev, T value)
        {
            int i = 0;
            if (!prev.Equals(value))
            {
                list[start] = value;
                if (start + length < list.Count && list[start + length] == null)
                    list[start + length] = prev;
                i++;
            }
            for (; i < length; i++)
                list[start + i] = default(T);
            if (start + length < list.Count && list[start + length] != null && list[start + length].Equals(value))
                list[start + length] = default(T);
            Text.Invalidate();
        }

        public void SetFontName(int start, int length, string value)
        {
            setList<string>(fontName, start, length, GetFontName(start - 1), value);
        }

        public void SetFontSize(int start, int length, float value)
        {
            setList<float?>(fontSize, start, length, GetFontSize(start - 1), value);
        }

        public void SetBold(int start, int length, bool value)
        {
            setList<bool?>(bold, start, length, GetBold(start - 1), value);
        }

        public void SetItalic(int start, int length, bool value)
        {
            setList<bool?>(italic, start, length, GetItalic(start - 1), value);
        }

        public void SetFill(int start, int length, Brush value)
        {
            setList<FillInfo?>(fill, start, length, GetFillInfo(start - 1), new FillInfo(value));
        }

        public void SetStroke(int start, int length, Pen value)
        {
            setList<StrokeInfo?>(stroke, start, length, GetStrokeInfo(start - 1), new StrokeInfo(value));
        }

        public string GetFontName(int index)
        {
            while (index >= 0)
            {
                if (fontName[index] != null)
                    return fontName[index];
                index--;
            }
            return Text.FontName;
        }

        public float GetFontSize(int index)
        {
            while (index >= 0)
            {
                if (fontSize[index] != null)
                    return (float)fontSize[index];
                index--;
            }
            return Text.FontSize;
        }

        public bool GetBold(int index)
        {
            while (index >= 0)
            {
                if (bold[index] != null)
                    return (bool)bold[index];
                index--;
            }
            return Text.Bold;
        }

        public bool GetItalic(int index)
        {
            while (index >= 0)
            {
                if (italic[index] != null)
                    return (bool)italic[index];
                index--;
            }
            return Text.Italic;
        }

        internal FillInfo GetFillInfo(int index)
        {
            while (index >= 0)
            {
                if (fill[index] != null)
                    return (FillInfo)fill[index];
                index--;
            }
            return new FillInfo(Text.Fill);
        }

        public Brush GetFill(int index)
        {
            return GetFillInfo(index).Fill;
        }

        internal StrokeInfo GetStrokeInfo(int index)
        {
            while (index >= 0)
            {
                if (stroke[index] != null)
                    return (StrokeInfo)stroke[index];
                index--;
            }
            return new StrokeInfo(Text.Stroke);
        }

        public Pen GetStroke(int index)
        {
            return GetStrokeInfo(index).Stroke;
        }

        public void Rebuild()
        {
            int i, j;

            lines.Clear();
            TextLine line = null;
            j = 0;
            string curFontName = GetFontName(-1);
            float curFontSize = GetFontSize(-1);
            bool curBold = GetBold(-1);
            bool curItalic = GetItalic(-1);
            FillInfo curFill = GetFillInfo(-1);
            StrokeInfo curStroke = GetStrokeInfo(-1);
            for (i = 0; i <= chars.Count; i++)
            {
                bool eol = i == chars.Count ||
                           (i < chars.Count && chars[i] == '\r') ||
                           (i < chars.Count && chars[i] == '\n');
                if (eol ||
                    fontName[i] != null ||
                    fontSize[i] != null ||
                    bold[i] != null ||
                    italic[i] != null ||
                    fill[i] != null ||
                    stroke[i] != null)
                {
                    if (i > j || (i == j && eol))
                    {
                        TextInfo textInfo;
                        textInfo.preserveWS = false;
                        textInfo.fill = curFill.Fill;
                        textInfo.stroke = curStroke.Stroke;
                        textInfo.transform = new Matrix();
                        textInfo.fontFamily = curFontName;
                        textInfo.fontSize = curFontSize;
                        textInfo.fontWeight = curBold ? FontStyle.Bold : FontStyle.Regular;
                        textInfo.fontStyle = curItalic ? FontStyle.Italic : FontStyle.Regular;
                        textInfo.vertical = Text.Vertical;
                        textInfo.rightToLeft = Text.RightToLeft;
                        textInfo.anchor = Text.Alignment;
                        switch (Text.Baseline)
                        {
                            default:
                            case "alphabetic":
                                textInfo.lineAlignment = StringAlignment.Far;
                                textInfo.addDescent = true;
                                break;
                            case "middle":
                                textInfo.lineAlignment = StringAlignment.Center;
                                textInfo.addDescent = false;
                                break;
                            case "text-after-edge":
                                textInfo.lineAlignment = StringAlignment.Far;
                                textInfo.addDescent = false;
                                break;
                            case "text-before-edge":
                                textInfo.lineAlignment = StringAlignment.Near;
                                textInfo.addDescent = false;
                                break;
                        }
                        TextSpan span = new TextSpan(textInfo, new string(chars.ToArray(), j, i - j));
                        if (line == null)
                            line = new TextLine(Text.Alignment, Text.Vertical, Text.RightToLeft);
                        line.AddSpan(span);
                    }
                    if (i < chars.Count)
                    {
                        if (fontName[i] != null)
                            curFontName = fontName[i];
                        if (fontSize[i] != null)
                            curFontSize = (float)fontSize[i];
                        if (bold[i] != null)
                            curBold = (bool)bold[i];
                        if (italic[i] != null)
                            curItalic = (bool)italic[i];
                        if (fill[i] != null)
                            curFill = (FillInfo)fill[i];
                        if (stroke[i] != null)
                            curStroke = (StrokeInfo)stroke[i];
                    }
                    j = i;
                    if (j < chars.Count && chars[j] == '\r')
                        j++;
                    if (j < chars.Count && chars[j] == '\n')
                        j++;
                    if (eol)
                    {
                        if (line != null)
                            lines.Add(line);
                        line = null;
                    }
                }
            }
        }

        public void Draw(Graphics g)
        {
            foreach (TextLine line in lines)
                line.Draw(g);
        }

        public string ToXml()
        {
            if (chars.Count == 0)
                return "";

            int i, j;

            StringBuilder sb = new StringBuilder();
            TextLine line = null;
            int nextLine = 0;
            IEnumerator<TextSpan> spans = null;

            string textFontName = Text.FontName;
            float textFontSize = Text.FontSize;
            bool textBold = Text.Bold;
            bool textItalic = Text.Italic;
            FillInfo textFill = new FillInfo(Text.Fill);
            StrokeInfo textStroke = new StrokeInfo(Text.Stroke);

            string curFontName = GetFontName(-1);
            float curFontSize = GetFontSize(-1);
            bool curBold = GetBold(-1);
            bool curItalic = GetItalic(-1);
            FillInfo curFill = GetFillInfo(-1);
            StrokeInfo curStroke = GetStrokeInfo(-1);

            j = 0;
            for (i = 0; i <= chars.Count; i++)
            {
                bool eol = i == chars.Count ||
                           (i < chars.Count && chars[i] == '\r') ||
                           (i < chars.Count && chars[i] == '\n');
                if (eol ||
                    fontName[i] != null ||
                    fontSize[i] != null ||
                    bold[i] != null ||
                    italic[i] != null ||
                    fill[i] != null ||
                    stroke[i] != null)
                {
                    bool newLine = false;
                    bool isSpan = false;
                    if (i > j || (i == j && eol))
                    {
                        if (line == null)
                        {
                            line = lines[nextLine];
                            nextLine++;
                            newLine = true;
                            spans = line.GetEnumerator();
                        }
                    }
                    if (i > j)
                    {
                        if (newLine)
                        {
                            sb.Append("<text");
                            sb.AppendFormat(" x=\"{0}\"", Svg.FloatToString(line.Position.X));
                            sb.AppendFormat(" y=\"{0}\"", Svg.FloatToString(line.Position.Y));

                            textFontName = Text.FontName;
                            textFontSize = Text.FontSize;
                            textBold = Text.Bold;
                            textItalic = Text.Italic;
                            textFill = new FillInfo(Text.Fill);
                            textStroke = new StrokeInfo(Text.Stroke);
                        }
                        
                        StringBuilder sbSpan = new StringBuilder();
                        if (textFontName != curFontName)
                            sbSpan.AppendFormat(" font-family=\"{0}\"", curFontName);
                        if (textFontSize != curFontSize)
                            sbSpan.AppendFormat(" font-size=\"{0}\"", Svg.FloatToString(curFontSize));
                        if (textBold != curBold)
                            if (curBold)
                                sbSpan.Append(" font-weight=\"bold\"");
                            else
                                sbSpan.Append(" font-weight=\"normal\"");
                        if (textItalic != curItalic)
                            if (curItalic)
                                sbSpan.Append(" font-style=\"italic\"");
                            else
                                sbSpan.Append(" font-style=\"normal\"");
                        if (textFill != curFill)
                        {
                            byte opacity = 255;
                            if (textFill.Fill != null)
                                opacity = (textFill.Fill as SolidBrush).Color.A;
                            else if (Text.Fill != null)
                                opacity = (Text.Fill as SolidBrush).Color.A;
                            sbSpan.Append(Svg.FillToString(curFill.Fill, opacity));
                        }
                        if (textStroke != curStroke)
                        {
                            byte opacity = 255;
                            if (textStroke.Stroke != null)
                                opacity = textStroke.Stroke.Color.A;
                            else if (Text.Stroke != null)
                                opacity = Text.Stroke.Color.A;
                            sbSpan.Append(Svg.StrokeToString(curStroke.Stroke, opacity));
                        }

                        StringBuilder sbEmbed = new StringBuilder();
                        if (spans.MoveNext() && spans.Current.Embedded)
                            sbEmbed.AppendFormat(" path=\"{0}\" box=\"{1},{2},{3},{4}\"", spans.Current.Path, Svg.FloatToString(spans.Current.Box.X), Svg.FloatToString(spans.Current.Box.Y), Svg.FloatToString(spans.Current.Box.Width), Svg.FloatToString(spans.Current.Box.Height));

                        if (newLine)
                        {
                            sb.Append(sbSpan.ToString());
                            sb.Append(">");
                            if (sbEmbed.Length > 0)
                            {
                                sb.AppendFormat("<tspan{0}>", sbEmbed.ToString());
                                isSpan = true;
                            }

                            textFontName = curFontName;
                            textFontSize = curFontSize;
                            textBold = curBold;
                            textItalic = curItalic;
                            textFill = curFill;
                            textStroke = curStroke;
                        }
                        else if (sbSpan.Length > 0 || sbEmbed.Length > 0)
                        {
                            sb.AppendFormat("<tspan{0}{1}>", sbSpan.ToString(), sbEmbed.ToString());
                            isSpan = true;
                        }

                        sb.Append(new string(chars.ToArray(), j, i - j).Replace("&", "&amp;").Replace("<", "&lt;").Replace(">", "&gt;"));
                        if (isSpan)
                            sb.Append("</tspan>");
                        if (eol)
                            sb.Append("</text>\n");
                    }
                    if (i < chars.Count)
                    {
                        if (fontName[i] != null)
                            curFontName = fontName[i];
                        if (fontSize[i] != null)
                            curFontSize = (float)fontSize[i];
                        if (bold[i] != null)
                            curBold = (bool)bold[i];
                        if (italic[i] != null)
                            curItalic = (bool)italic[i];
                        if (fill[i] != null)
                            curFill = (FillInfo)fill[i];
                        if (stroke[i] != null)
                            curStroke = (StrokeInfo)stroke[i];
                    }
                    j = i;
                    if (j < chars.Count && chars[j] == '\r')
                        j++;
                    if (j < chars.Count && chars[j] == '\n')
                        j++;
                    if (eol)
                        line = null;
                }
            }
            return sb.ToString();
        }

        public TextFormatted(Text Text, XPathNavigator nav)
        {
            this.Text = Text;

            TextInfo areaInfo = TextInfo.Default;
            areaInfo.LoadInfo(nav);
            areaInfo.transform = new Matrix();
            TextInfo prevInfo = areaInfo;

            XPathNodeIterator it = nav.Select("text");
            while (it.MoveNext())
            {
                if (chars.Count > 0)
                    Replace(chars.Count, 0, "\r\n");

                TextLine line = new TextLine(areaInfo, it.Current);
                lines.Add(line);

                foreach (TextSpan span in line)
                {
                    int start = chars.Count;
                    int length = span.Text.Length;
                    Replace(start, 0, span.Text);

                    TextInfo textInfo = span.TextInfo;
                    if (prevInfo.fontFamily != textInfo.fontFamily)
                        SetFontName(start, length, textInfo.fontFamily);
                    if (prevInfo.fontSize != textInfo.fontSize)
                        SetFontSize(start, length, textInfo.fontSize);
                    if (prevInfo.fontWeight != textInfo.fontWeight)
                        SetBold(start, length, textInfo.fontWeight == FontStyle.Bold);
                    if (prevInfo.fontStyle != textInfo.fontStyle)
                        SetItalic(start, length, textInfo.fontStyle == FontStyle.Italic);
                    if (new FillInfo(prevInfo.fill) != new FillInfo(textInfo.fill))
                        SetFill(start, length, textInfo.fill);
                    if (new StrokeInfo(prevInfo.stroke) != new StrokeInfo(textInfo.stroke))
                        SetStroke(start, length, textInfo.stroke);
                    prevInfo = textInfo;
                }
            }
        }
    }

    public class Text : EditableObject
    {
        public readonly TextFormatted Formatted;

        public Point Position;
        public bool DoNotScale = false;
        public string Type = "";

        private string fontName = "comic";
        private float fontSize = 10f;
        private bool bold = false;
        private bool italic = false;
        private Brush fill = new SolidBrush(Color.Black);
        private Pen stroke = null;

        private bool vertical = false;
        private bool rightToLeft = false;
        private StringAlignment alignment = StringAlignment.Near;
        private string baseline = "alphabetic";
        private float lineSpacing = 0f;
        private float rotateDegrees = 0;

        private bool valid = true;
        private RectangleF rect;

        public Text(Page Page, Point Position) : base(Page, 1)
        {
            Formatted = new TextFormatted(this);
            this.Position = Position;
        }

        public string FontName
        {
            get
            {
                return fontName;
            }
            set
            {
                fontName = value;
                Invalidate();
            }
        }

        public float FontSize
        {
            get
            {
                return fontSize;
            }
            set
            {
                fontSize = value;
                Invalidate();
            }
        }

        public bool Bold
        {
            get
            {
                return bold;
            }
            set
            {
                bold = value;
                Invalidate();
            }
        }

        public bool Italic
        {
            get
            {
                return italic;
            }
            set
            {
                italic = value;
                Invalidate();
            }
        }

        public Brush Fill
        {
            get
            {
                return fill;
            }
            set
            {
                fill = value;
                Invalidate();
            }
        }

        public Pen Stroke
        {
            get
            {
                return stroke;
            }
            set
            {
                stroke = value;
                Invalidate();
            }
        }

        public bool Vertical
        {
            get
            {
                return vertical;
            }
            set
            {
                vertical = value;
                Invalidate();
            }
        }

        public bool RightToLeft
        {
            get
            {
                return rightToLeft;
            }
            set
            {
                rightToLeft = value;
                Invalidate();
            }
        }

        public StringAlignment Alignment
        {
            get
            {
                return alignment;
            }
            set
            {
                alignment = value;
                Invalidate();
            }
        }

        public string Baseline
        {
            get
            {
                return baseline;
            }
            set
            {
                baseline = value;
                Invalidate();
            }
        }

        public float LineSpacing
        {
            get
            {
                return lineSpacing;
            }
            set
            {
                lineSpacing = value;
                Invalidate();
            }
        }

        public float RotateDegrees
        {
            get
            {
                return rotateDegrees;
            }
            set
            {
                rotateDegrees = value;
                Invalidate();
            }
        }


        public void Invalidate()
        {
            valid = false;
            Page.Changed = true;
        }

        public override void BeginEdit()
        {
            base.BeginEdit();
            Page.Changed = true;
        }

        public override void EndEdit()
        {
            base.EndEdit();
            Page.Changed = true;
        }

        public bool Test(Point point)
        {
            if (!valid)
                Rebuild();
            PointF p = new PointF(point.X - Position.X, point.Y - Position.Y);
            if (rotateDegrees != 0f)
            {
                PointF[] pts = new PointF[] { p };
                Matrix m = new Matrix();
                m.Rotate(rotateDegrees);
                m.Invert();
                m.TransformVectors(pts);
                p = pts[0];
            }

            return rect.Contains(p);
        }

        public void Rebuild()
        {
            int i;
            valid = true;
            rect = RectangleF.Empty;

            if (vertical && !rightToLeft)
                throw new NotImplementedException();

            Formatted.Rebuild();
            if (Formatted.Count == 0)
                return;

            RebuildBox();

            PointF pos = PointF.Empty;
            if (vertical)
                pos.X = Formatted[0].Box.Width + Formatted[0].Box.X;
            else
                pos.Y = Formatted[0].Box.Y;
            for (i = 0; i < Formatted.Count; i++)
            {
                RectangleF lineRect = Formatted[i].Box;
                if (vertical)
                    pos.X -= lineRect.Width + lineRect.X;
                else
                    pos.Y -= lineRect.Y;
                Formatted[i].Position = pos;
                if (vertical)
                    pos.X += lineRect.X - lineSpacing;
                else
                    pos.Y += lineRect.Y + lineRect.Height + lineSpacing;
            }
        }

        private void RebuildBox()
        {
            int i;
            rect = Formatted[0].Box;
            for (i = 1; i < Formatted.Count; i++)
            {
                RectangleF lineRect = Formatted[i].Box;
                if (vertical)
                {
                    rect.X -= lineSpacing;
                    rect.X -= lineRect.Width;
                    rect.Width += lineSpacing;
                    rect.Width += lineRect.Width;
                    if (rect.Height < lineRect.Height)
                        rect.Height = lineRect.Height;
                }
                else
                {
                    rect.Height += lineSpacing;
                    rect.Height += lineRect.Height;
                    if (rect.Width < lineRect.Width)
                    {
                        if (rightToLeft)
                            rect.X = -lineRect.Width;
                        rect.Width = lineRect.Width;
                    }
                }
            }
            if (alignment == StringAlignment.Center)
                if (vertical)
                    rect.Y -= rect.Height/2;
                else if (rightToLeft)
                    rect.X += rect.Width/2;
                else
                    rect.X -= rect.Width/2;
            if (alignment == StringAlignment.Far)
                if (vertical)
                    rect.Y -= rect.Height;
                else if (rightToLeft)
                    rect.X += rect.Width;
                else
                    rect.X -= rect.Width;
        }

        public override void Draw(Graphics Graphics)
        {
            if (!valid)
                Rebuild();

            if (DoNotScale)
                Graphics.TranslateTransform(Page.Transform(Position).X, Page.Transform(Position).Y);
            else
                Graphics.Transform = Page.GetTransform(Position);
            if (rotateDegrees != 0f)
                Graphics.RotateTransform(rotateDegrees);
            Formatted.Draw(Graphics);
            Graphics.Transform = new Matrix();

            if (Editing)
            {
                Matrix m = Page.GetTransform(Position);
                float len = 5/m.Elements[0];
                if (rotateDegrees != 0f)
                    m.Rotate(rotateDegrees);

                PointF p0 = new PointF(rect.X, 0);
                PointF p1 = new PointF(p0.X - len, p0.Y - len);
                PointF p2 = new PointF(p0.X - len, p0.Y + len);
                if (vertical)
                {
                    p0 = new PointF(0, rect.Y);
                    p1 = new PointF(p0.X - len, p0.Y - len);
                    p2 = new PointF(p0.X + len, p0.Y - len);
                }
                else if (rightToLeft)
                {
                    p0 = new PointF(rect.Right, 0);
                    p1 = new PointF(p0.X + len, p0.Y - len);
                    p2 = new PointF(p0.X + len, p0.Y + len);
                }

                PointF[] ptfs = new PointF[] { p0, p1, p2, rect.Location, new PointF(rect.Right, rect.Y), new PointF(rect.Right, rect.Bottom), new PointF(rect.X, rect.Bottom), rect.Location };
                m.TransformPoints(ptfs);

                Point[] arrow = new Point[3];
                for (int i = 0; i < 3; i++)
                    arrow[i] = new Point((int)ptfs[i].X, (int)ptfs[i].Y);

                Graphics.DrawLine(DashedLine.penEven, arrow[1], arrow[0]);
                Graphics.DrawLine(DashedLine.penOdd, arrow[1], arrow[0]);
                Graphics.DrawLine(DashedLine.penEven, arrow[2], arrow[0]);
                Graphics.DrawLine(DashedLine.penOdd, arrow[2], arrow[0]);

                Point[] box = new Point[5];
                for (int i = 0; i < 5; i++)
                    box[i] = new Point((int)ptfs[i + 3].X, (int)ptfs[i + 3].Y);

                Graphics.DrawLines(DashedLine.penEven, box);
                Graphics.DrawLines(DashedLine.penOdd, box);
            }
        }

        public string ToXml()
        {
            if (!valid)
                Rebuild();

            StringBuilder sb = new StringBuilder();

            sb.Append("<TextArea");
            if (Type != "")
                sb.AppendFormat(" Type=\"{0}\"", Type);
            sb.AppendFormat(" font-family=\"{0}\"", fontName);
            sb.AppendFormat(" font-size=\"{0}\"", Svg.FloatToString(fontSize));
            if (bold)
                sb.Append(" font-weight=\"bold\"");
            if (italic)
                sb.Append(" font-style=\"italic\"");
            sb.Append(Svg.FillToString(fill));
            sb.Append(Svg.StrokeToString(stroke));
            if (vertical)
                sb.Append(" writing-mode=\"tb\"");
            else if (rightToLeft)
                sb.Append(" writing-mode=\"rl\"");
            if ((baseline != "alphabetic" || vertical) && (baseline != "middle" || !vertical))
                sb.AppendFormat(" dominant-baseline=\"{0}\"", baseline);
            if (alignment == StringAlignment.Center)
                sb.Append(" text-anchor=\"middle\"");
            if (alignment == StringAlignment.Far)
                sb.Append(" text-anchor=\"end\"");
            sb.AppendFormat(" transform=\"translate({0},{1})", Position.X, Position.Y);
            if (rotateDegrees != 0f)
                sb.AppendFormat(" rotate({0})", Svg.FloatToString(rotateDegrees));
            sb.Append("\">\n");

            sb.Append(Formatted.ToXml());

            sb.Append("</TextArea>\n");
            return sb.ToString();
        }

        public Text(Page Page, XPathNavigator nav) : base(Page, 1)
        {
            int i, j;
            string value;

            Type = nav.GetAttribute("Type", "");

            if ((value = nav.GetAttribute("font-family", "")) != "")
                fontName = value;
            if ((value = nav.GetAttribute("font-size", "")) != "")
                fontSize = Svg.ParseFloat(value);
            if ((value = nav.GetAttribute("font-weight", "")) != "" && value == "bold")
                bold = true;
            if ((value = nav.GetAttribute("font-style", "")) != "" && value == "italic")
                italic = true;
            fill = Svg.ParseFill(nav);
            stroke = Svg.ParseStroke(nav);
            if ((value = nav.GetAttribute("writing-mode", "")) != "")
            {
                vertical = value == "tb";
                rightToLeft = value == "rl" || value == "tb";
            }
            if (vertical)
                baseline = "middle";
            if ((value = nav.GetAttribute("dominant-baseline", "")) != "")
                baseline = value;
            if ((value = nav.GetAttribute("text-anchor", "")) != "")
                if (value == "middle")
                    alignment = StringAlignment.Center;
                else if (value == "end")
                        alignment = StringAlignment.Far;
            value = nav.GetAttribute("transform", "");
            i = value.IndexOf("translate");
            if (i >= 0)
            {
                j = value.IndexOf("(", i);
                i = j + 1;
                j = value.IndexOf(",", i);
                Position.X = Svg.ParseInt(value.Substring(i, j - i).Trim());
                i = j + 1;
                j = value.IndexOf(")", i);
                Position.Y = Svg.ParseInt(value.Substring(i, j - i).Trim());
            }
            i = value.IndexOf("rotate");
            if (i >= 0)
            {
                j = value.IndexOf("(", i);
                i = j + 1;
                j = value.IndexOf(")", i);
                rotateDegrees = Svg.ParseFloat(value.Substring(i, j - i).Trim());
            }

            Formatted = new TextFormatted(this, nav);
            valid = true;

            if (Formatted.Count > 1)
            {
                float spacing = 0.0f;
                PointF pos = PointF.Empty;
                if (vertical)
                    pos.X = Formatted[0].Box.Width + Formatted[0].Box.X;
                else
                    pos.Y = Formatted[0].Box.Y;
                for (i = 0; i < Formatted.Count; i++)
                {
                    RectangleF lineRect = Formatted[i].Box;
                    if (vertical)
                        pos.X -= lineRect.Width + lineRect.X;
                    else
                        pos.Y -= lineRect.Y;
                    if (vertical)
                        spacing += pos.X - Formatted[i].Position.X;
                    else
                        spacing += Formatted[i].Position.Y - pos.Y;
                    pos = Formatted[i].Position;
                    if (vertical)
                        pos.X += lineRect.X;
                    else
                        pos.Y += lineRect.Y + lineRect.Height;
                }
                lineSpacing = spacing/(Formatted.Count - 1);
            }

            if (Formatted.Count > 0)
                RebuildBox();
        }
    }
}
